import { IAppDEUIAction } from '@ibiz/model-core'
import {
  IUIActionResult,
  IUILogicParams,
  FrontUIActionProvider,
  CodeListItem
} from '@ibiz-template/runtime'
import { RuntimeModelError } from '@ibiz-template/core'
import { saveAs } from 'file-saver'
import ExcelJS from 'exceljs'

/**
 * 数据导出行为适配器
 *
 * @export
 * @class DataExportUIActionProvider
 * @extends {FrontUIActionProvider}
 */
export class DataExportUIActionProvider extends FrontUIActionProvider {
  /**
   *上下文
   *
   * @private
   * @type {IContext}
   * @memberof DataExportUIActionProvider
   */
  private context!: IContext

  /**
   * 视图参数
   *
   * @private
   * @type {IParams}
   * @memberof DataExportUIActionProvider
   */
  private params!: IParams

  /**
   * 代码表Map
   *
   * @private
   * @type {Map<string, CodeListItem[]>}
   * @memberof DataExportUIActionProvider
   */
  private codeListMap: Map<string, readonly CodeListItem[]> = new Map()

  async execAction(action: IAppDEUIAction, args: IUILogicParams): Promise<IUIActionResult> {
    const actionResult: IUIActionResult = {
      cancel: true
    }
    this.context = args.context
    this.params = args.params
    if (action.navigateParams) {
      let model: IData | null = null
      try {
        action.navigateParams.forEach((params) => {
          if (params.key === 'dataexportmodel' && params.value) {
            model = JSON.parse(params.value) as IData
          }
        })
      } catch (error) {
        throw new RuntimeModelError(action, 'dataexportmodel 配置错误')
      }
      if (model) {
        await this.dataExport(action.name!, model)
      } else {
        throw new RuntimeModelError(action, '未配置 dataexportmodel')
      }
    }
    return actionResult
  }

  /**
   * 数据导出
   *
   * @param {string} fileName
   * @param {IData} model
   * @memberof DataExportUIActionProvider
   */
  async dataExport(fileName: string, model: IData) {
    const sheetNames: string[] = Object.keys(model)
    if (sheetNames.length > 0) {
      // 创建工作簿
      const workbook = new ExcelJS.Workbook()
      for (const name of sheetNames) {
        // 添加工作表
        const worksheet = workbook.addWorksheet(model[name].caption)
        const column = model[name].column
        if (column) {
          await this.initCodeListMap(model[name])
          const wsColumns: IData[] = []
          Object.keys(column).forEach((key) => {
            const col = {
              header: column[key],
              key
            }
            if (model[name].width && model[name].width[key]) {
              Object.assign(col, {
                width: model[name].width[key]
              })
            }
            wsColumns.push(col)
          })
          worksheet.columns = wsColumns
          worksheet.addRows([{}])
          worksheet.duplicateRow(2, 40, true)
          this.setDataValidation(model[name], worksheet)
        }
      }
      const buffer = await workbook.xlsx.writeBuffer()
      saveAs(
        new Blob([buffer], {
          type: 'application/octet-stream'
        }),
        `${fileName}.xlsx`
      )
    }
  }

  /**
   * 设置值校验
   *
   * @param {IData} sheetModel 工作表配置
   * @param {ExcelJS.Worksheet} worksheet 工作表实例
   * @memberof DataExportUIActionProvider
   */
  private setDataValidation(sheetModel: IData, worksheet: ExcelJS.Worksheet) {
    const columnNames: string[] = sheetModel.codeList ? Object.keys(sheetModel.codeList) : []
    if (columnNames.length > 0) {
      columnNames.forEach((name) => {
        const codeList = this.codeListMap.get(`${sheetModel.codeName}_${name}`)
        if (codeList) {
          const formulae = codeList.map((item) => item.text).join(',')
          const column = worksheet.getColumn(name)
          if (column) {
            column.eachCell({ includeEmpty: true }, function (cell, rowNumber) {
              if (rowNumber !== 1) {
                cell.dataValidation = {
                  type: 'list',
                  allowBlank: true,
                  formulae: [`"${formulae}"`]
                }
              }
            })
          }
        }
      })
    }
  }

  /**
   * 初始化代码表
   *
   * @param {IData} sheetModel
   * @memberof DataExportUIActionProvider
   */
  private async initCodeListMap(sheetModel: IData) {
    const columnNames: string[] = sheetModel.codeList ? Object.keys(sheetModel.codeList) : []
    if (columnNames.length > 0) {
      for (const columnName of columnNames) {
        const codeList = this.codeListMap.get(`${sheetModel.codeName}_${columnName}`)
        if (!codeList) {
          const codeListItems = await this.loadCodeList(sheetModel.codeList[columnName])
          this.codeListMap.set(`${sheetModel.codeName}_${columnName}`, codeListItems)
        }
      }
    }
  }

  /**
   * 加载代码表数据
   *
   * @param {string} appCodeListId 代码表标识
   * @return {*}  {Promise<readonly}
   * @memberof DataImportUIActionProvider
   */
  private async loadCodeList(appCodeListId: string): Promise<readonly CodeListItem[]> {
    const app = await ibiz.hub.getApp(this.context.srfappid)
    let dataItems: readonly CodeListItem[] = []
    dataItems = await app.codeList.get(appCodeListId, this.context, this.params)
    return dataItems
  }
}
